﻿#pragma once
#include"com.hpp"
#include"FixedMemoryPool.hpp"
#include"PageMap.hpp"

class PageCache {
public:
	~PageCache()
	{
	}

	static PageCache* getPageCache()
	{
		return &_PCache;
	}

	Span* newSpan(size_t PageCount)
	{
		if (PageCount > 128)//如果需要的页数大于128,直接去堆上申请
		{
			void* ptr = FetchFromHeap(PageCount);
			//Span* newspan = new Span;
			Span* newspan = _SpanPool.New();

			newspan->_pageNum = (PAGENUM)ptr >> PAGE_SHIFT;
			newspan->_pageCount = PageCount;
            //再将这个分配出去的span首页位置记录下来，便于通过页号找到span
            _PageWithSpanMap.set(newspan->_pageNum, newspan);
			return newspan;
		}

		//1.如果有直接返回存在的span
		if (!_PageBucket[PageCount - 1].Empty())
		{
			Span* span = _PageBucket[PageCount - 1].PopHead();

            for (size_t j = 0;j < span->_pageCount;j++)//将要给上层的span的页号和span指针记录到PageWithSpanMap中(为centralCache层服务)
            {
                _PageWithSpanMap.set(span->_pageNum + j, span);
            }
			return span;
		}
		//2.如果没有就寻找大的Span桶并切分为我们需要大小的Span
		int i = PageCount;
		while (i <= PageBucketNum)
		{
			if (!_PageBucket[i - 1].Empty())
			{
				Span* span1 = _PageBucket[i - 1].PopHead();//切分出来想要给上层的部分
				//Span* span2 = new Span;//切分出来剩余的部分
				Span* span2 = _SpanPool.New();

				span2->_pageNum = span1->_pageNum + PageCount;
				span2->_pageCount = span1->_pageCount - PageCount;
				_PageBucket[span2->_pageCount - 1].PushHead(span2);

				span1->_pageCount = PageCount;
				span1->_prev = nullptr;
				span1->_next = nullptr;

                //我们将切分出来挂在PageBucket上的span的首尾两个页放入map中，让其可以进行页合并
                _PageWithSpanMap.set(span2->_pageNum, span2);
                _PageWithSpanMap.set(span2->_pageNum + span2->_pageCount - 1, span2);

				for (size_t j = 0;j < span1->_pageCount;j++)//将要给上层的span的页号和span指针记录到PageWithSpanMap中(为centralCache层服务)
				{
                    _PageWithSpanMap.set(span1->_pageNum + j, span1);
				}

				return span1;
			}
			i++;
		}
		//3.如果遍历整个PageBucket都没有找到空的Span，那么我们就需要从堆上获取一个128页的Span
		//Span* newspan = new Span;
		Span* newspan = _SpanPool.New();

		void* PHeap = FetchFromHeap(PageBucketNum);//从堆上获取空间
		newspan->_pageCount = PageBucketNum;//初始化span
		newspan->_pageNum = (PAGENUM)PHeap >> PAGE_SHIFT;//利用获取的空间初始化span
		_PageBucket[PageBucketNum - 1].PushHead(newspan);//最后将这个申请的span挂入到PageBucket中

		return newSpan(PageCount);
	}

    Span* FindBelongSpan(void* ptr)//通过地址找到页号，再通过页号找到对应的Span（无锁查找）
    {
        PAGENUM pageNum = (PAGENUM)ptr >> PAGE_SHIFT;
        return (Span*)_PageWithSpanMap.get(pageNum);
    }

	void BackToPageCache(Span* span)
	{
		if (span->_pageCount > 128)//如果span的页数大于128直接还给堆
		{
			void* ptr = (void*)(span->_pageNum << PAGE_SHIFT);
			BackToHeap(ptr);
			//delete span;
			_SpanPool.Delete(span);
			return;
		}
		while (true)
		{
			PAGENUM prev = span->_pageNum - 1;
            Span* prevSpan = (Span*)_PageWithSpanMap.get(prev);
            if (prevSpan == nullptr)
			{
				//说明没有这个span，不需要继续向前合并了
				break;
			}
            //找到前面相邻的span
			if (prevSpan->_Isuse)//如果这个span被使用了,也不需要合并了
			{
				break;
			}
			if (prevSpan->_pageCount + span->_pageCount > PageBucketNum)//如果大于128也无法合并
			{
				break;
			}
			span->_pageNum = prevSpan->_pageNum;
			span->_pageCount += prevSpan->_pageCount;

			_PageBucket[prevSpan->_pageCount].erase(prevSpan);
			//delete prevSpan;
			_SpanPool.Delete(prevSpan);
		}

		while (true)
		{
			PAGENUM next = span->_pageNum + span->_pageCount;
            Span* nextSpan = (Span*)_PageWithSpanMap.get(next);
            if (nextSpan == nullptr)
			{
				//说明没有这个span，不需要继续向后合并了
				break;
			}
            //找到后面相邻的span
			if (nextSpan->_Isuse)
			{
				break;
			}
			if (nextSpan->_pageCount + span->_pageCount > PageBucketNum)
			{
				break;
			}
			span->_pageCount += nextSpan->_pageCount;

			_PageBucket[nextSpan->_pageCount].erase(nextSpan);
			//delete nextSpan;
			_SpanPool.Delete(nextSpan);
		}

        _PageBucket[span->_pageCount - 1].PushHead(span);
        span->_Isuse = false;
        _PageWithSpanMap.set(span->_pageNum, span);
        _PageWithSpanMap.set(span->_pageNum + span->_pageCount - 1, span);
	}

	std::mutex _mtx;
private:
    PageCache()
    {
    }

	PageCache(const PageCache&) = delete;
	PageCache operator =(const PageCache&) = delete;

	SpanList _PageBucket[PageBucketNum];
	FixedMemoryPool<Span> _SpanPool;

    PageMap _PageWithSpanMap; // 基数树映射：页号 → Span*

	static PageCache _PCache;
};

PageCache PageCache::_PCache;